package com.zy.je.common.core.utils.xml;

import com.sun.xml.bind.marshaller.CharacterEscapeHandler;
import com.zy.je.common.core.exception.BizException;
import io.swagger.annotations.ApiModelProperty;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.experimental.SuperBuilder;
import lombok.extern.slf4j.Slf4j;
import org.dromara.hutool.core.text.StrUtil;

import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Marshaller;
import javax.xml.bind.Unmarshaller;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlElementWrapper;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.XmlTransient;
import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;
import java.io.IOException;
import java.io.StringReader;
import java.io.StringWriter;
import java.io.Writer;
import java.util.List;

/**
 * xml转换工具
 */
@Slf4j
public class XmlUtil {
    final static String DEFAULT_FRAGMENT = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n";

    /**
     * 将对象直接转换成String类型的 XML输出
     * @param obj
     * @return
     */
    @Deprecated
    public static String toXmlString(Object obj) {
        return toXml(obj, DEFAULT_FRAGMENT);
    }
    /**
     * 将String类型的xml转换成对象
     */
    @Deprecated
    public static <T> T parseObject(Class<T> clazz, String xml) {
        return fromXml(xml, clazz);
    }

    /**
     * 将对象直接转换成String类型的 XML输出
     * @param obj
     * @param fragment
     * @return
     */
    public static String toXml(Object obj, String fragment) {
        try {
            // 利用jdk中自带的转换类实现
            JAXBContext context = JAXBContext.newInstance(obj.getClass());

            Marshaller marshaller = context.createMarshaller();
            // 格式化xml输出的格式
            //编码格式
            marshaller.setProperty(Marshaller.JAXB_ENCODING, "UTF-8");
            //是否格式化生成xml串
            marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE);
            //去掉生成xml的默认报文头。
            marshaller.setProperty(Marshaller.JAXB_FRAGMENT, Boolean.TRUE);

            //转换所有的适配字符，包括xml实体字符&lt;和&gt;，xml实体字符在好多处理xml的框架中是处理不了的，除非序列化。
            marshaller.setProperty("com.sun.xml.bind.characterEscapeHandler",
                    new CharacterEscapeHandler() {
                        @Override
                        public void escape(char[] ch, int start, int length, boolean isAttVal,
                                           Writer writer) throws IOException {
                            writer.write(ch, start, length);
                        }
                    });
            // 创建输出流
            StringWriter sw = new StringWriter();
            // 将对象转换成输出流形式的xml
            //添加自己想要的xml报文头
            if (!StrUtil.isBlank(fragment)) {
                sw.write(fragment);
            }
            marshaller.marshal(obj, sw);
            return sw.toString();
        } catch (JAXBException e) {
            log.error("转换数据出错({})：{}", e.getMessage(), e);
            throw new BizException(e);
        }
    }

    /**
     * 将String类型的xml转换成对象
     */
    public static <T> T fromXml(String xml, Class<T> clazz) {
        try {
            JAXBContext context = JAXBContext.newInstance(clazz);
            // 进行将Xml转成对象的核心接口
            Unmarshaller unmarshaller = context.createUnmarshaller();
            StringReader sr = new StringReader(xml);
            Object xmlObject = unmarshaller.unmarshal(sr);
            return (T) xmlObject;
        } catch (JAXBException e) {
            log.error("转换数据出错({})：{}", xml, e.getMessage(), e);
            throw new BizException(e);
        }
    }


    /**
     * 将对象直接转换成String类型的 XML输出
     *
     * @param obj
     * @return
     */
    public static String toXml(Object obj) {
        return toXml(obj, DEFAULT_FRAGMENT);
    }


    private static void logInfo(String s) {
        log.info("\n" + s);
    }


    @NoArgsConstructor
    @AllArgsConstructor
    @SuperBuilder
    @Getter
    @XmlRootElement(name = "program")
    public static class LoginReq  {
        @XmlJavaTypeAdapter(value = CDataAdapter.class)
        private String bizId;

        private String functionId;
        private String sessionId;

        @XmlElement(name = "userid")
        private String userid;

        @XmlElement(name = "password")
        private String password;

        @XmlElement(name = "currenttime")
        private String currenttime;


        public String getBizId() {
            return bizId;
        }

        @XmlTransient
        public void setBizId(String bizId) {
            this.bizId = bizId;
        }

        public String getFunctionId() {
            return functionId;
        }

        @XmlElement(name = "function_id")
        public void setFunctionId(String functionId) {
            this.functionId = functionId;
        }

        public String getSessionId() {
            return sessionId;
        }

        @XmlElement(name = "session_id")
        public void setSessionId(String sessionId) {
            this.sessionId = sessionId;
        }


//    <program>
//    <function_id>sys0001</function_id>
//    <userid>004001</userid>
//    <password>CD14B3815B998F0CE9EB12F7E0A394C5</password>
//    <currenttime>20220419110741</currenttime>
//</program>
    }


    @NoArgsConstructor
    @AllArgsConstructor
    @SuperBuilder
    @Getter
    @XmlRootElement(name = "program")
    public static class Bizh110001Resp  {
        @XmlElement(name = "return_code")
        private int returnCode;
        @XmlElement(name = "return_code_message")
        private String returnCodeMessage;

        @ApiModelProperty(value = "个人信息")
        @XmlElementWrapper(name="personinfo")
        @XmlElement(name = "row")
        List<PersoninfoRow> personinfo;

        @ApiModelProperty(value = "基金信息")
        @XmlElementWrapper(name="freezeinfo")
        @XmlElement(name = "row")
        List<FreezeinfoRow> freezeinfo;

        @ApiModelProperty(value = "门慢申请信息")
        @XmlElementWrapper(name="spinfo")
        @XmlElement(name = "row")
        List<SpinfoRow> spinfo;

        @ApiModelProperty(value = "上次住院信息")
        @XmlElementWrapper(name="lastbizinfo")
        @XmlElement(name = "row")
        List<LastbizinfoRow> lastbizinfo;

//    @NoArgsConstructor
//    @AllArgsConstructor
//    @SuperBuilder
//    @Getter
//    public static class Personinfo {
//        @XmlElement(name = "row")
//        List<PersoninfoRow> row;
//    }
//    @NoArgsConstructor
//    @AllArgsConstructor
//    @SuperBuilder
//    @Getter
//    public static class Freezeinfo {
//        @XmlElement(name = "row")
//        List<FreezeinfoRow> row;
//    }
//    @NoArgsConstructor
//    @AllArgsConstructor
//    @SuperBuilder
//    @Getter
//    public static class Spinfo {
//        @XmlElement(name = "row")
//        List<SpinfoRow> row;
//    }
//
//    @NoArgsConstructor
//    @AllArgsConstructor
//    @SuperBuilder
//    @Getter
//    public static class Lastbizinfo {
//        @XmlElement(name = "row")
//        List<LastbizinfoRow> row;
//    }


        @NoArgsConstructor
        @AllArgsConstructor
        @SuperBuilder
        @Getter
        public static class LastbizinfoRow {
            @ApiModelProperty(value = "就医地行政区划代码")
            @XmlElement(name = "aaa027")
            private String aaa027;

            @ApiModelProperty(value = "参保人所属行政区划编码")
            @XmlElement(name = "baa027")
            private String baa027;

            @ApiModelProperty(value = "医药机构编码")
            @XmlElement(name = "akb020")
            private String akb020;

            @ApiModelProperty(value = "医药机构名称")
            @XmlElement(name = "akb021")
            private String akb021;

            @ApiModelProperty(value = "业务类型")
            @XmlElement(name = "aka130")
            private String aka130;

            @ApiModelProperty(value = "就诊类别")
            @XmlElement(name = "bka006")
            private String bka006;

            @ApiModelProperty(value = "险种类别")
            @XmlElement(name = "aae140")
            private String aae140;

            @ApiModelProperty(value = "个人编号")
            @XmlElement(name = "aac001")
            private String aac001;

            @ApiModelProperty(value = "社会保障号码（对中国国籍的国内居民为公民身份号码，对外国人、港澳台人员、华侨等为统一分配的社会保障号码）")
            @XmlElement(name = "aac002")
            private String aac002;

            @ApiModelProperty(value = "姓名")
            @XmlElement(name = "aac003")
            private String aac003;

            @ApiModelProperty(value = "性别（字典参数：字典参数Ins_Aac004。1：男；2：女；9：未说明性别）")
            @XmlElement(name = "aac004")
            private String aac004;
            @ApiModelProperty(value = "人员类别（字典参数：Ins_Bka035。见码表）")
            @XmlElement(name = "bka035")
            private String bka035;

            @ApiModelProperty(value = "开始日期（入院日期，格式：yyyyMMdd）")
            @XmlElement(name = "aae030")
            private Integer aae030;

            @ApiModelProperty(value = "结束日期（出院日期，格式：yyyyMMdd）")
            @XmlElement(name = "aae031")
            private Integer aae031;

            @ApiModelProperty(value = "结算时间（格式：yyyyMMddhh24miss (24小时)）")
            @XmlElement(name = "akc194")
            private String akc194;

            @ApiModelProperty(value = "入院诊断")
            @XmlElement(name = "akc193")
            private String akc193;

            @ApiModelProperty(value = "入院诊断名称")
            @XmlElement(name = "bkz101")
            private String bkz101;

            @ApiModelProperty(value = "出院诊断")
            @XmlElement(name = "akc196")
            private String akc196;

            @ApiModelProperty(value = "出院诊断名称")
            @XmlElement(name = "bkz102")
            private String bkz102;

            @ApiModelProperty(value = "就诊科室")
            @XmlElement(name = "akf001")
            private String akf001;

            @ApiModelProperty(value = "就诊科室名称")
            @XmlElement(name = "bka020")
            private String bka020;

            @ApiModelProperty(value = "病区编码")
            @XmlElement(name = "bka021")
            private String bka021;

            @ApiModelProperty(value = "病区名称")
            @XmlElement(name = "bka022")
            private String bka022;

            @ApiModelProperty(value = "床位")
            @XmlElement(name = "ake020")
            private String ake020;
        }

        @NoArgsConstructor
        @AllArgsConstructor
        @SuperBuilder
        @Getter
        public static class SpinfoRow {
            @ApiModelProperty(value = "门慢申请序列号")
            @XmlElement(name = "aaz267")
            private String aaz267;

            @ApiModelProperty(value = "就诊类别（字典参数：Ins_Bka006。见码表）")
            @XmlElement(name = "bka006")
            private String bka006;

            @ApiModelProperty(value = "就诊类别名称")
            @XmlElement(name = "bka345")
            private String bka345;

            @ApiModelProperty(value = "业务类型（字典参数：Ins_Aka130。见码表）")
            @XmlElement(name = "aka130")
            private String aka130;

            @ApiModelProperty(value = "疾病编码")
            @XmlElement(name = "akc193")
            private String akc193;

            @ApiModelProperty(value = "疾病名称")
            @XmlElement(name = "bkz101")
            private String bkz101;

            @ApiModelProperty(value = "申请生效时间（格式：yyyyMMdd）")
            @XmlElement(name = "aae030")
            private Integer aae030;
            @ApiModelProperty(value = "申请到期时间（格式：yyyyMMdd）")
            @XmlElement(name = "aae031")
            private Integer aae031;
        }



        @NoArgsConstructor
        @AllArgsConstructor
        @SuperBuilder
        @Getter
        public static class FreezeinfoRow {
            @ApiModelProperty(value = "基金款项代码")
            @XmlElement(name = "aaa157")
            private String aaa157;

            @ApiModelProperty(value = "基金名称")
            @XmlElement(name = "aad006")
            private String aad006;

            @ApiModelProperty(value = "基金状态标志。字典参数Ins_Bka888（0：正常；1：冻结；2：暂停参保；3：中止参保；9：未参保）")
            @XmlElement(name = "bka888")
            private String bka888;
        }


        @NoArgsConstructor
        @AllArgsConstructor
        @SuperBuilder
        @Getter
        public static class PersoninfoRow {
            @ApiModelProperty(value = "个人编号")
            @XmlElement(name = "aac001")
            private String aac001;

            @ApiModelProperty(value = "社会保障号码（对中国国籍的国内居民为公民身份号码，对外国人、港澳台人员、华侨等为统一分配的社会保障号码）")
            @XmlElement(name = "aac002")
            private String aac002;

            @ApiModelProperty(value = "姓名")
            @XmlElement(name = "aac003")
            private String aac003;

            @ApiModelProperty(value = "性别。字典参数Ins_Aac004（1：男；2：女；9：未说明性别）")
            @XmlElement(name = "aac004")
            private String aac004;

            @ApiModelProperty(value = "出生日期（格式：yyyyMMdd）")
            @XmlElement(name = "aac006")
            private String aac006;

            @ApiModelProperty(value = "参保地行政区划代码（字典参数Ins_Baa027。见码表：baa027）")
            @XmlElement(name = "baa027")
            private String baa027;

            @ApiModelProperty(value = "人员类别（字典参数Ins_Bka035。见码表）")
            @XmlElement(name = "bka035")
            private String bka035;

            @ApiModelProperty(value = "用工形式（字典参数Ins_Aac013。见码表）")
            @XmlElement(name = "aac013")
            private String aac013;

            @ApiModelProperty(value = "行政职务级别（字典参数Ins_Bac001。见码表）")
            @XmlElement(name = "bac001")
            private String bac001;

            @ApiModelProperty(value = "联系电话")
            @XmlElement(name = "aae005")
            private String aae005;

            @ApiModelProperty(value = "单位编码")
            @XmlElement(name = "aab001")
            private String aab001;

            @ApiModelProperty(value = "单位名称")
            @XmlElement(name = "bka008")
            private String bka008;

            @ApiModelProperty(value = "险种类型（字典参数Ins_Aae140。见码表）")
            @XmlElement(name = "aae140")
            private String aae140;

            @ApiModelProperty(value = "特殊人员对象类型。字典参数Ins_Bka910。（参见代码定义bka910）")
            @XmlElement(name = "bka910")
            private String bka910;

            @ApiModelProperty(value = "备用字段1")
            @XmlElement(name = "bka923")
            private String bka923;
            @ApiModelProperty(value = "备用字段2")
            @XmlElement(name = "bka924")
            private String bka924;
            @ApiModelProperty(value = "备用字段3")
            @XmlElement(name = "bka925")
            private String bka925;
            @ApiModelProperty(value = "备用字段4")
            @XmlElement(name = "bka926")
            private String bka926;
        }

    }

    public static void main(String[] args) {
        LoginReq loginReq = LoginReq.builder()
                .functionId("1232")
                .bizId("bbbbbbbbbbb")
                .sessionId("sss")
                .userid("uuu2")
                .build();
        logInfo(XmlUtil.toXmlString(loginReq));
        String s = """
                <?xml version="1.0" encoding="utf-8"?>
                <program>
                   <spinfo/>
                   <lastbizinfo>
                       <row>
                           <aae140>350</aae140>
                           <aac001>300050725</aac001>
                           <aac002>440102196104130625</aac002>
                           <ake020>001</ake020>
                           <baa027>440100</baa027>
                           <bka035>2</bka035>
                           <bkz102>面部损伤</bkz102>
                           <bkz101>面部损伤</bkz101>
                           <aac003>谭定宜</aac003>
                           <aac004>2</aac004>
                           <aaa027>440100</aaa027>
                           <akb021>广州市第一人民医院</akb021>
                           <akb020>004001</akb020>
                           <aka130>62</aka130>
                           <aae031>20240410</aae031>
                           <akc196>S09.901</akc196>
                           <aae030>20240410</aae030>
                           <akc194>2024-04-11 09:43:20.0</akc194>
                           <akc193>S09.901</akc193>
                           <akf001>zy0000135</akf001>
                           <bka022>口腔颌面外科病房</bka022>
                           <bka006>620</bka006>
                           <bka021>zy0000135</bka021>
                           <bka020>口腔颌面外科病房</bka020>
                       </row>
                   </lastbizinfo>
                   <freezeinfo>
                       <row>
                           <aad006>现金</aad006>
                           <aaa157>999998</aaa157>
                           <bka888>0</bka888>
                       </row>
                       <row>
                           <aad006>基本医疗保险统筹基金</aad006>
                           <aaa157>350100</aaa157>
                           <bka888>0</bka888>
                       </row>
                       <row>
                           <aad006>补充医疗保险基金</aad006>
                           <aaa157>350300</aaa157>
                           <bka888>0</bka888>
                       </row>
                       <row>
                           <aad006>大病医疗保险基金</aad006>
                           <aaa157>350806</aaa157>
                           <bka888>0</bka888>
                       </row>
                   </freezeinfo>
                   <return_code>1</return_code>
                   <return_code_message>服务执行正确</return_code_message>
                   <personinfo>
                       <row>
                           <aae140>350</aae140>
                           <aac001>300050725</aac001>
                           <aac002>440102196104130625</aac002>
                           <aac013>10</aac013>
                           <aab001>200006403</aab001>
                           <bka924/>
                           <bka923/>
                           <bka008>广州市医疗保险服务中心</bka008>
                           <bka910>00</bka910>
                           <bka888>0</bka888>
                           <baa027>440100</baa027>
                           <bac028>0027432</bac028>
                           <bka926/>
                           <bka925/>
                           <bka035>2</bka035>
                           <bac001>1</bac001>
                           <bac034>2</bac034>
                           <aac006>19610413</aac006>
                           <aac003>谭定宜</aac003>
                           <aac004>2</aac004>
                           <aae005/>
                       </row>
                   </personinfo>
                </program>
                """;

        Bizh110001Resp resp = XmlUtil.parseObject(Bizh110001Resp.class, s);

        logInfo("xxx");
    }

}